/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.service.impl;

import com.bifrost.constant.*;
import com.bifrost.constant.OrderType;
import com.bifrost.entity.*;
import com.bifrost.event.CouponUpdatedEvent;
import com.bifrost.exception.BonusPointsNotEnoughException;
import com.bifrost.exception.CancleConsumeException;
import com.bifrost.exception.ConsumeCouponException;
import com.bifrost.game.entity.LotteryAward;
import com.bifrost.game.entity.LotteryGame;
import com.bifrost.game.entity.LotteryGameWinner;
import com.bifrost.game.repository.LotteryGameWinnerRepository;
import com.bifrost.game.service.LotteryGameService;
import com.bifrost.model.CouponResponse;
import com.bifrost.repository.*;
import com.bifrost.service.BonusPointsService;
import com.bifrost.service.CouponService;
import com.bifrost.service.CustomerService;
import com.bifrost.uid.UidGenerator;
import com.bifrost.util.DateUtils;
import com.bifrost.util.LocalDateUtils;
import com.bifrost.wrapper.CouponWrapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 *
 *
 *@author joewee
 *@version 1.0.0
 *@Date 2017/11/7 19:59
 */
@Service
public class CouponServiceImpl implements CouponService {
    private static final Logger logger = LoggerFactory.getLogger(CouponServiceImpl.class);
    private static final ReentrantLock lock = new ReentrantLock();//不公平的重入锁，防止超领优惠券
    @Autowired
    CouponDetailRepository couponDetailRepository;
    @Autowired
    CustomerRepository customerRepository;
    @Autowired
    ProductSkuRepository productSkuRepository;
    @Autowired
    CouponRepository couponRepository;
    @Autowired
    UidGenerator defaultUidGenerator;
    @Autowired
    OrderCouponRepository orderCouponRepository;
    @Autowired
    BonusPointsService bonusPointsService;
    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    CouponGrantAutoRepository couponGrantAutoRepository;
    @Autowired
    MerchantStoreRepository merchantStoreRepository;
    @Autowired
    CouponHistroyRepository couponHistroyRepository;
    @Autowired
    LotteryGameService gameService;
    @Autowired
    CouponService couponService;
    @Autowired
    CustomerService customerService;
    @Autowired
    RedissonClient redisson;
    @Autowired
    LotteryGameWinnerRepository winnerRepository;
    @Autowired
    OrderReturnItemRepository returnItemRepository;
    /**
     * 获取会员已使用的优惠券
     *
     * @param customer 会员
     * @return
     */
    @Override
    public List<CouponDetail> getUsedCoupons(Customer customer) {
        return couponDetailRepository.findAllUsedByCustomer(customer);
    }

    /**
     * 获取会员所有未使用的优惠券
     *
     * @param customer 会员
     * @return  未使用的优惠券
     */
    @Override
    public List<CouponDetail> getUnusedCoupons(Customer customer) {

        List<CouponDetail> couponDetails = couponDetailRepository.findAllUnusedByCustomer(customer);
        return couponDetails.stream().filter(detail -> {
           return CouponStatus.UNUSED.equalsIgnoreCase(detail.getCouponStatus())&&!isExpiration(detail)&&!isReturn(detail);
        }).collect(Collectors.toList());
    }

    private boolean isReturn(CouponDetail detail) {
        //现金购买优惠券 判断在退款
        if(CouponReleaseMode.CASH_PURCHASE.equals(detail.getReleaseMode())){
            OrderReturnItem returnItem = returnItemRepository.findFirstByCouponCode(detail.getCouponCode());
            if(returnItem == null){
                return false;
            }
            OrderReturn orderReturn = returnItem.getOrderReturn();
            if(!OrderReturnStatus.CANCELED.equalsIgnoreCase(orderReturn.getStatus())){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取会员已过期的优惠券
     *
     * @param customer 会员
     * @return
     */
    @Override
    public List<CouponDetail> getExpiredCoupons(Customer customer) {
       List<CouponDetail> expired = couponDetailRepository.findAllByCustomerAndCouponStatus(customer,CouponStatus.EXPIRED);
       List<CouponDetail> couponDetails = couponDetailRepository.findAllUnusedByCustomer(customer);
       List<CouponDetail> unusedButExpired=null;
       if(couponDetails!=null&&!couponDetails.isEmpty()){
           unusedButExpired= couponDetails.stream().filter(couponDetail->{
               return isExpiration(couponDetail);
           }).collect(Collectors.toList());
       }
       if(expired==null){
           return unusedButExpired;
       }else if(unusedButExpired==null){
           return expired;
       }else{
          expired.addAll(unusedButExpired);
          return expired;
       }

    }

    /**
     * 根据订单验证用户所有优惠券是否可用
     *
     * @param order
     * @return
     */
    @Override
    public List<CouponWrapper> getCoupons(Order order) {
        Customer customer = order.getCustomer();
        List<CouponDetail> coupons = getUnusedCoupons(customer);
        if(coupons.isEmpty()){
            return null;
        }
        //按面值排序，默认最大值为默认
        List<CouponWrapper> wrappers = sortByValue(validate(order,coupons));
       return wrappers;
    }

    /**
     * 结合订单验证所有优惠券的可用状态
     *
     * @param order
     * @param list
     * @return
     */
    @Override
    public List<CouponWrapper> validate(Order order, List<CouponDetail> list) {
        if(list.isEmpty()){
            return null;
        }
        List<CouponWrapper> wrappers=new ArrayList<>(list.size());
        list.stream().forEach(couponDetail->{
            CouponWrapper wrapper = null;
            wrapper = validate(order,couponDetail);
            if(wrapper!=null){
              wrappers.add(wrapper);
          }
        });
       return  wrappers;
    }

    /**
     * 验证订单是否适用该优惠券
     *
     * @param order
     * @param couponDetail
     * @return
     */
    @Override
    public CouponWrapper validate(Order order, CouponDetail couponDetail)  {
        //验证该订单用户与优惠券用户一致否
        if(!couponDetail.getCustomer().equals(order.getCustomer())){
            return null;
        }
        Coupon coupon = couponDetail.getCoupon();
        List<CouponRule> rules = coupon.getCouponRules();
        List<CouponMerchant> merchants =coupon.getCouponMerchants();
        List<CouponExclude> excludes = coupon.getCouponExcludes();
        CouponWrapper wrapper =new CouponWrapper(coupon,couponDetail,rules,excludes,merchants,order,false);
        Date startDate = couponDetail.getActiveStartDate();
        LocalDateTime startTime = LocalDateUtils.dateTimeFromDate(startDate);
        LocalDateTime now =LocalDateTime.now();
        if(now.isBefore(startTime))
        {
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("优惠券未到使用时间");
            return wrapper;
        }
        else if(isExpiration(couponDetail)){
            if(!CouponStatus.EXPIRED.equals(couponDetail.getCouponStatus())){
                couponDetail.setCouponStatus(CouponStatus.EXPIRED);
                couponDetailRepository.save(couponDetail);
            }
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("优惠券已过期");
            return wrapper;
        }
        return validateWithCouponType(wrapper);
    }

    /**
     * 根据优惠券类型验证
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateWithCouponType(CouponWrapper wrapper)  {
       CouponDetail detail = wrapper.getDetail();
       String typeCode = detail.getCouponTypeCode();
       if(StringUtils.isBlank(typeCode)){
           wrapper.setAvailable(false);
           wrapper.setUnavailableReason("不可用");
           return wrapper;
       }
       if (CouponTypes.MERCHANT_COUPON.getType().equals(typeCode)){
         return  validateMerchantCoupon(wrapper);
       }
       else if (CouponTypes.DISCOUNT_COUPON.getType().equals(typeCode)){
           return  validateDiscountCoupon(wrapper);
       }
       else if (CouponTypes.CASH_COUPON.getType().equals(typeCode)){
           return  validateCashCoupon(wrapper);
       }
       else if (CouponTypes.UPKEEP_COUPON.getType().equals(typeCode)){
           return  validateUpkeepCoupon(wrapper);
       }
       else if (CouponTypes.DECORATION_COUPON.getType().equals(typeCode)){
           return  validateDecorationCoupon(wrapper);
       }
       else if (CouponTypes.CATEGORY_COUPON.getType().equals(typeCode)){
           return  validateCategoryCoupon(wrapper);
       }
       else if (CouponTypes.SKU_COUPON.getType().equals(typeCode)){
           return  validateSkuCoupon(wrapper);
       }
       else if (CouponTypes.SHIPMENT_COUPON.getType().equals(typeCode)){
           return  validateShipmentCoupon(wrapper);
       }
       else{
           wrapper.setAvailable(false);
           wrapper.setUnavailableReason("不可用");
       }
        return wrapper;
    }

    /**
     * 验证店铺劵<br>
     *只验证店铺以及使用条件
     * @param wrapper
     * @return
     */
    private CouponWrapper validateMerchantCoupon(CouponWrapper wrapper)  {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用该店铺");
            return  wrapper;
        }
        wrapper.setMatchedItems (wrapper.getOrder().getOrderItems());
        if(!meetTheRequirement(wrapper)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单不满足使用条件");
            return  wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;
    }

    /**
     * 验证满减劵
     * @param wrapper
     * @return
     */

    private CouponWrapper validateDiscountCoupon(CouponWrapper wrapper)  {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
      String orderType =  detail.getForOrderType();
      Order order = wrapper.getOrder();

      //只有基本订单类型才可以使用满减劵
      if(orderType!=null&&!orderType.equals(order.getOrderType())){
          wrapper.setAvailable(false);
          wrapper.setUnavailableReason("不适用于该订单类型");
          return wrapper;
      }
        wrapper.setMatchedItems (order.getOrderItems());
        if (!meetTheRequirement(wrapper)) {
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单金额不满足条件");
            return  wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;
        /*BigDecimal total = wrapper.getMatchedItems().stream().map(OrderItem::getSkuPrice).reduce(BigDecimal.ZERO,BigDecimal::add);
      //判断使用条件
        if(total==null||BigDecimal.ZERO.equals(total)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单金额不满足条件");
            logger.warn("订单金额为0或空");
            return wrapper;
        }
        //校验订单金额门槛
        BigDecimal minTotal = detail.getOrderMinTotal();
        if(minTotal!=null&&total.compareTo(minTotal)<0){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单金额不满足条件");
            return wrapper;
        }
        if(total.compareTo(detail.getDiscountAmount())<0){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单金额小于优惠券金额");
            return wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;*/

    }

    /**
     * 验证现金劵
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateCashCoupon(CouponWrapper wrapper) {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
        String orderType =  detail.getForOrderType();
        Order order = wrapper.getOrder();

        //只有基本订单类型才可以使用满减劵
        if(orderType!=null&&!orderType.equals(order.getOrderType())){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该订单类型");
            return wrapper;
        }
        //验证券金额必须小于订单金额
        List<OrderItem>itemModels =order.getOrderItems();
        BigDecimal total = BigDecimal.ZERO;
        for(OrderItem item :itemModels){
            total =total.add(item.getSkuPrice().multiply(BigDecimal.valueOf(item.getSkuQuantity())));
        }
        if(total.compareTo(detail.getDiscountAmount())<0){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单金额小于优惠券金额");
            return wrapper;
        }
        wrapper.setMatchedItems (wrapper.getOrder().getOrderItems());
        wrapper.setAvailable(true);
        return  wrapper;
    }

    /**
     * 验证保养券
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateUpkeepCoupon(CouponWrapper wrapper)  {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
        String orderType =  detail.getForOrderType();
        Order order = wrapper.getOrder();

        //只有保养订单类型才可以使用满减劵
        if(!OrderType.UPKEEP.equals(orderType)||!OrderType.UPKEEP.equals(order.getOrderType())){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该订单类型");
            return wrapper;
        }
        wrapper.setMatchedItems (wrapper.getOrder().getOrderItems());
        //判断订单是否满足使用条件
        if(!meetTheRequirement(wrapper)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单不满足使用条件");
            return  wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;
    }

    /**
     * 验证美容券
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateDecorationCoupon(CouponWrapper wrapper) {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
        String orderType =  detail.getForOrderType();
        Order order = wrapper.getOrder();

        //只有美容订单类型才可以使用满减劵
        if(!OrderType.DECORATION.equals(orderType)||!OrderType.DECORATION.equals(order.getOrderType())){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该订单类型");
            return wrapper;
        }
        wrapper.setMatchedItems (wrapper.getOrder().getOrderItems());
        //判断订单是否满足使用条件
        if(!meetTheRequirement(wrapper)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单不满足使用条件");
            return  wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;
    }

    /**
     * 验证品类券
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateCategoryCoupon(CouponWrapper wrapper) {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
        String couponOrderType =  detail.getForOrderType();
        Order order = wrapper.getOrder();
        String orderType =order.getOrderType();
        //只有订单类型匹配才可以使用品类劵
        if(couponOrderType!=null&&!orderType.equals(couponOrderType)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该订单类型");
            return wrapper;
        }
        CouponWrapper matchedWrapper = matchCategory(wrapper);
        //没有满足品类设置的商品
        if(!matchedWrapper.isAvailable()){
            return  matchedWrapper;
        }
        CouponWrapper finalWrapper = removeExcludeSku(matchedWrapper);
        //判断订单是否满足使用条件
        if(!meetTheRequirement(finalWrapper)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单不满足使用条件");
            return  wrapper;
        }
        finalWrapper.setAvailable(true);
        return  finalWrapper;
    }

    /**
     * 验证单品券
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper validateSkuCoupon(CouponWrapper wrapper) {
        boolean hasMatch =isMatchMerchantStore(wrapper);
        if(!hasMatch){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该店铺");
            return wrapper;
        }
        CouponDetail detail = wrapper.getDetail();
        String couponOrderType =  detail.getForOrderType();
        Order order = wrapper.getOrder();
        String orderType =order.getOrderType();
        //只有订单类型匹配才可以使用品类劵
        if(couponOrderType!=null&&!orderType.equals(couponOrderType)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("不适用于该订单类型");
            return wrapper;
        }
       List<OrderItem> items = order.getOrderItems();
        List<CouponRule> rules =  wrapper.getRules();
        //未设置规则
        if(rules==null||rules.isEmpty()){
            //单品券未设置单品则不可用
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定商品");
            return  wrapper;
        }
        List<Long> skuRules =  rules.stream().map(CouponRule::getForTarget).collect(Collectors.toList());
        List<Long> skus= items.stream().map(OrderItem::getSkuId).collect(Collectors.toList());
        if(skuRules==null|| skuRules.isEmpty()){
            logger.warn("优惠券{0}配置异常",detail.getCoupon().getCouponId());
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定商品");
            return wrapper;
        }
        if(skus == null || skus.isEmpty()){
            logger.warn("订单异常");
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定商品");
            return wrapper;
        }
        List<Long> matchedSku = (List<Long>) CollectionUtils.intersection(skuRules,skus);
        List<OrderItem> matchedItems = items.stream().filter(itemModel -> {
            return matchedSku.contains(itemModel.getSkuId());
        }).collect(Collectors.toList());
        if(matchedItems==null||matchedItems.isEmpty()){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定商品");
            return wrapper;
        }
        wrapper.setMatchedItems(matchedItems);
        //判断订单是否满足使用条件
        if(!meetTheRequirement(wrapper)){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("订单不满足使用条件");
            return  wrapper;
        }
        wrapper.setAvailable(true);
        return  wrapper;

    }

    /**
     * 验证免邮券
     *
     * @param wrapper
     * @return
     */
    private CouponWrapper validateShipmentCoupon(CouponWrapper wrapper) {
        //TODO 验证免邮劵逻辑
        wrapper.setAvailable(false);
        return wrapper;
    }

    /**
     * 是否适用于优惠券对应店铺
     *
     * @param wrapper
     * @return
     */
    private boolean isMatchMerchantStore(CouponWrapper wrapper) {
        List<CouponMerchant> couponMerchants = wrapper.getMerchants();
        //未设置优惠券使用门店默认全场通用
        if(couponMerchants==null||couponMerchants.isEmpty()){
            return  true;
        }
        Order order = wrapper.getOrder();
        Long merchantStore = order.getServiceShop();
        //Long merchantStore = order.getMerchantStore().getMerchantId();
        return couponMerchants.stream().anyMatch(couponMerchant -> {
            return couponMerchant.getMerchantStore().getMerchantId().equals(merchantStore);
        });
    }

    /**
     * 匹配满足优惠券品类设置的所有商品
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper matchCategory(CouponWrapper wrapper) {
      Order order =  wrapper.getOrder();
      if(order==null){
          return null;
      }
      List<OrderItem> items = order.getOrderItems();
      List<CouponRule> rules =  wrapper.getRules();
      //未设置规则
      if(rules==null||rules.isEmpty()){
          wrapper.setAvailable(true);
            return  wrapper;
      }
      List<Long> categoryRules =  rules.stream().map(CouponRule::getForTarget).collect(Collectors.toList());
      List<Long> itemsCategory= items.stream().map(OrderItem::getCategoryId).collect(Collectors.toList());
        if (itemsCategory == null || itemsCategory.isEmpty()) {
            logger.warn("订单sku找不到有效的品类");
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定品类商品");
            return wrapper;
        } else if (categoryRules==null || categoryRules.isEmpty()) {
            logger.warn("品类劵配置异常");
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定品类商品");
            return wrapper;
        }
        List<Long> retainsCollection = (List<Long>)CollectionUtils.intersection(categoryRules,itemsCategory);
        List<OrderItem> matchedItems = items.stream().filter(item -> {
            return retainsCollection.contains(productSkuRepository.findOne(item.getSkuId()).getCategory().getCategoryId() );
        }).collect(Collectors.toList());
        if(matchedItems==null||matchedItems.isEmpty()){
            wrapper.setAvailable(false);
            wrapper.setUnavailableReason("仅限特定品类商品");
            return wrapper;
        }
        wrapper.setMatchedItems (matchedItems);
        wrapper.setAvailable(true);
        return  wrapper;
    }

    /**
     * 移除不适用品类劵的特定商品
     *
     * @param wrapper
     * @return
     */

    private CouponWrapper removeExcludeSku(CouponWrapper wrapper) {
       List<CouponExclude> excludes = wrapper.getExcludes();
       if(excludes==null||excludes.isEmpty()){
           wrapper.setAvailable(true);
           return  wrapper;
       }
      List<Long> excludeSkus =  excludes.stream().map(CouponExclude::getExcludeTargetId).collect(Collectors.toList());
       Order order = wrapper.getOrder();
       List<OrderItem> items =order.getOrderItems().stream().filter(item -> {
            return !excludeSkus.contains(item.getSkuId());
        }).collect(Collectors.toList());
        wrapper.setMatchedItems(items);
        return wrapper;
    }

    /**
     * 是否满足使用条件
     *
     * @param wrapper
     * @return
     */

    private boolean meetTheRequirement(CouponWrapper wrapper)  {
        Order order = wrapper.getOrder();
        //判断使用条件
        //获取订单中符合条件的商品
        List<OrderItem> itemModels =wrapper.getMatchedItems();
        if(itemModels==null||itemModels.isEmpty()){
            return  false;
        }
        CouponDetail detail = wrapper.getDetail();
        //校验订单数量门槛
        Integer quantiyMin= detail.getRelatedTargetQuantity();
        Integer quantiyTotal = itemModels.stream().map(OrderItem::getSkuQuantity).reduce(0,Integer::sum);
        if(quantiyMin!=null&&quantiyTotal.compareTo(quantiyMin)<0){
            return  false;
        }
        //校验订单金额门槛
        BigDecimal minTotal = detail.getOrderMinTotal();
        BigDecimal total = BigDecimal.ZERO;
        for(OrderItem item :itemModels){
            total =total.add(item.getSkuPrice().multiply(BigDecimal.valueOf(item.getSkuQuantity())));
        }
        if(minTotal!=null&&total.compareTo(minTotal)<0){
            return false;
        }
        if(total.compareTo(detail.getDiscountAmount())<0){
            return false;
        }
        if(!itemModels.isEmpty()){
            //分摊优惠金额到商品行项目
            BigDecimal sumShared=BigDecimal.ZERO;
            for(int i=0;i<itemModels.size();i++){
                OrderItem item =itemModels.get(i);
                BigDecimal subTotal =total.add(item.getSkuPrice().multiply(BigDecimal.valueOf(item.getSkuQuantity())));
                item.setDiscountShare(detail.getDiscountAmount().multiply(subTotal.divide(total,2))) ;
                sumShared = sumShared.add(item.getDiscountShare());
            }
            BigDecimal remains= detail.getDiscountAmount().subtract(sumShared);
            itemModels.get(0).setDiscountShare(itemModels.get(0).getDiscountShare().add(remains));
        }
        return  true;
    }
    /**
     * 验证优惠券是否过期
     *
     * @param detail
     * @return
     */
    @Override
    public boolean isExpiration(CouponDetail detail) {
        if(CouponStatus.EXPIRED.equalsIgnoreCase(detail.getCouponStatus())){
            return false;
        }
        Date endDate = detail.getActiveEndDate();
        LocalDateTime endTime = LocalDateUtils.dateTimeFromDate(endDate);
        LocalDateTime now =LocalDateTime.now();
        if(now.isAfter(endTime)){
            try {
                if(!CouponStatus.EXPIRED.equals(detail.getCouponStatus())){
                    detail.setCouponStatus(CouponStatus.EXPIRED);
                    couponDetailRepository.save(detail);
                }
            } catch (Exception e) {
                logger.error("设置优惠券过期时失败",e);
            }
            return true;
        }
        return false;
    }

    /**
     * 默认优惠券(按面值排序)
     *
     * @param wrappers
     * @return
     */
    @Override
    public List<CouponWrapper> sortByValue(List<CouponWrapper> wrappers) {
        if(wrappers!=null&&!wrappers.isEmpty()){
            return wrappers.stream().sorted(new Comparator<CouponWrapper>() {
                @Override
                public int compare(CouponWrapper o1, CouponWrapper o2) {
                    return o2.getDetail().getDiscountAmount().compareTo(o1.getDetail().getDiscountAmount());
                }
            }).collect(Collectors.toList());
        }
        return null;

    }

    /**
     * 根据优惠券码获取优惠券
     *
     * @param codes
     * @param customer
     * @return
     */
    @Override
    public List<CouponDetail> getCustomerCouponByCode(Customer customer , Set<Long> codes) {
        return couponDetailRepository.findAllByCustomerAndCouponCodeIn(customer,codes);
    }

    /**
     * 查询会员已使用指定优惠券的最大数量
     *
     * @param customer
     * @param coupon
     * @return
     */
    @Override
    public int getCustomerMaxUsed(Customer customer, Coupon coupon) {
        List<CouponDetail> couponDetails = couponDetailRepository.findAllByCustomerAndIsUsedAndCoupon(customer,Boolean.TRUE,coupon);
        if(couponDetails==null&&couponDetails.isEmpty()){
            return 0;
        }
        return couponDetails.size();
    }
    @Override
    @Transactional(rollbackFor = {Exception.class,RuntimeException.class})
    public CouponResponse getCoupon(Customer customer, Long id, SendCouponType sendCouponType) throws BonusPointsNotEnoughException {
        Coupon coupon =null;
        coupon= couponRepository.findOne(id);
        if (coupon==null||Boolean.FALSE.equals(coupon.getIsEnabled())) {
            return new CouponResponse(coupon,null,false,"啊哦,优惠券已经下架,下次要早点来哦");
        }
        else if(coupon!=null&&isBetweenReleasePeriod(coupon)){
            //TODO 验证该用户是否能领取该优惠券
            Integer count =  countCoupons(customer,coupon);
            /**
             * 用户领取次数小于优惠券单用户领取次数限制
             * **/
            if(coupon.getMaxUsesPerCustomer().compareTo(count)>0){

                if(SendCouponType.BASIC.equals(sendCouponType)){
                    Integer cnt = couponDetailRepository.countByCustomerAndCouponAndCouponStatus(customer,coupon,CouponStatus.UNUSED);
                    if(cnt!=null&&cnt>0){
                        return   new CouponResponse(coupon,null,false,"您已经存在该类型优惠券，请使用后再领取");
                    }
                }else if(SendCouponType.LOTTERY.equals(sendCouponType)){}

                if(coupon.getQuantityAvailable()>0){
                    BigInteger bonusPoints = coupon.getBonusPoints();
                    Long uuid = defaultUidGenerator.getUID();
                    //需要积分
                    if(bonusPoints!=null && bonusPoints.compareTo(BigInteger.ZERO)>0){
                        // 需要积分扣减积分
                       BigInteger points = customer.getBonusPoints();
                       if(points==null || points.compareTo(bonusPoints)<0){
                            throw new BonusPointsNotEnoughException(String.format("积分不足：%d",points==null?0:points));
                       }else{
                           bonusPointsService.decrementBonusPoints(customer,String.format("%d",uuid),null,bonusPoints,BonusPointsSource.MALL,BonusPointsUpdatedDetail.EXCHANGE_COUPON,"积分兑换优惠券");
                       }
                    }
                    CouponDetail couponDetail =newCouponDetail(coupon);
                    couponDetail.setCoupon(coupon);
                    couponDetail.setCustomer(customer);
                    couponDetail.setCouponCode(uuid);
                    couponDetail.setIsUsed(Boolean.FALSE);
                    couponDetail.setGeneratedTime(new Date());
                    /**
                     * 有效期为领取之日起
                     * */
                    if(ValidityType.DAYS.equalsIgnoreCase(couponDetail.getValidityType())){
                        couponDetail.setActiveStartDate(LocalDateUtils.fromLocalDateTime(LocalDateTime.now()));
                        couponDetail.setActiveEndDate(LocalDateUtils.fromLocalDateTime(LocalDateTime.now().plus(coupon.getValidityDays(),ChronoUnit.DAYS)));
                    }
                    //coupon.setQuantityAvailable(coupon.getQuantityAvailable()-1);
                    if(couponRepository.decrementCouponQuantityAvailable(id,1)==1){
                        couponDetailRepository.save(couponDetail);
                        //保存优惠券购买记录
                        Long transactionId = defaultUidGenerator.getUID();
                        CouponHistroy histroy = new CouponHistroy(transactionId,
                                couponDetail.getCouponDetailId(),
                                customer.getCustomerId(),
                                null,
                                CouponHistroy.Operation.NEW,
                                String.format("购买优惠券：核销码%s",couponDetail.getCouponCode()));
                        couponHistroyRepository.save(histroy);
                        CouponUpdatedEvent evt = new CouponUpdatedEvent(couponDetail);
                        applicationContext.publishEvent(evt);
                        return new CouponResponse(coupon,couponDetail,true,null);

                    }else{
                        return  new CouponResponse(coupon,null,false,"领取失败,优惠券数量不足");
                    }
                }else{
                    return  new CouponResponse(coupon,null,false,"领取失败,优惠券数量不足");
                }
            }else{
                return  new CouponResponse(coupon,null,false,"不要太贪心哦，您已经领取过该优惠券");
            }
        }else{
            return new CouponResponse(coupon,null,false,"哇！您来的太早了，活动还没有开始呢");
        }
        //return new CouponResponse(coupon,null,false,"领取失败，系统开了个小差");
    }

    /**
     * 获取指定类型的优惠券
     *
     * @param type
     * @return
     */
    @Override
    public List<Coupon> getCoupons(String type, MerchantStore store) {
        if(StringUtils.isEmpty(type)){
            return null;
        }
        List<Coupon> coupons=null;

        coupons = couponRepository.findAllByCouponTypeCodeAndReleaseBy(type,store);
        /*
        //店铺券
        if(CouponTypes.MERCHANT_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.MERCHANT_COUPON.getType(),store);
        }
        //满减券
        else if(CouponTypes.DISCOUNT_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.DISCOUNT_COUPON.getType(),store);
        }
        //代金劵
        else if(CouponTypes.CASH_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.CASH_COUPON.getType(),store);
        }
        //保养券
        else if(CouponTypes.UPKEEP_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.UPKEEP_COUPON.getType(),store);
        }
        //美容劵
        else if(CouponTypes.DECORATION_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.DECORATION_COUPON.getType(),store);
        }
        //品类券
        else if(CouponTypes.CATEGORY_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.CATEGORY_COUPON.getType(),store);
        }
        //单品券
        else if(CouponTypes.SKU_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.SKU_COUPON.getType(),store);
        }
        //邮费劵
        else if(CouponTypes.SHIPMENT_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.SHIPMENT_COUPON.getType(),store);
        }
        //全场通用券
        else if(CouponTypes.ALL_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.ALL_COUPON.getType(),store);
        }
        //线下使用券
        else if(CouponTypes.OFFLINE_COUPON.getType().equalsIgnoreCase(type)){
            coupons= couponRepository.findAllByCouponTypeCodeAndReleaseBy(CouponTypes.OFFLINE_COUPON.getType(),store);
        }
        */
        if(coupons!=null){
            coupons=  coupons.stream().filter(coupon -> {
                return isAvailable(coupon)&&!isAutoRelease(coupon);
            }).collect(Collectors.toList());
        return coupons;
        }
        return null;
    }

    @Override
    public boolean isBetweenReleasePeriod(Coupon coupon){
        if(coupon!=null){
            Date startDate = coupon.getReleaseDateStart();
            Date endDate = coupon.getReleaseDateEnd();
            LocalDateTime now =  LocalDateTime.now();
            if(startDate!=null&&endDate!=null){
                /**
                 * 发放优惠券时间之内
                 * */
                if(now.isBefore(LocalDateUtils.dateTimeFromDate(endDate))&&now.isAfter(LocalDateUtils.dateTimeFromDate(startDate))){
                    return true;
                }
            }
        }
        return false;
    }
    protected boolean isAvailable(Coupon coupon){
        if(Boolean.TRUE.equals(coupon.getIsEnabled())){
            //if(coupon.getQuantityAvailable()!=null&&coupon.getQuantityAvailable().compareTo(0)>0){
                /*
                if(isBetweenReleasePeriod(coupon)){
                    return true;
                }
                */
                if(isBeforeReleaseEnd(coupon) && isAuthed(coupon)){
                    return true;
                }
            //}
        }
        return false;
    }

    private boolean isAuthed(Coupon coupon) {
        if(CouponAuthStatus.AUTHED.equalsIgnoreCase(coupon.getAuthStatus()) || coupon.getAuthStatus() == null){
            return true;
        }
        return false;
    }

    private boolean isBeforeReleaseEnd(Coupon coupon) {
        if(coupon!=null){
            Date endDate = coupon.getReleaseDateEnd();
            LocalDateTime now =  LocalDateTime.now();
            if(endDate!=null){
                /**
                 * 发放优惠券结束时间之前
                 * */
                if(now.isBefore(LocalDateUtils.dateTimeFromDate(endDate))){
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean isAutoRelease(Coupon coupon){
        return  CouponReleaseMode.AUTO.equalsIgnoreCase(coupon.getReleaseMode());
    }
    private   Integer countCoupons(Customer customer, Coupon coupon){
       return couponDetailRepository.countByCustomerAndCoupon(customer,coupon);
    }

    /**
     * 统计会员有多少优惠券
     * @param customer
     * @return
     */
    @Override
    public  int countCoupons(Customer customer){
         List coupons = getUnusedCoupons(customer);
         return coupons==null?0:coupons.size();
    }

    /**
     * 使用优惠券
     *
     * @param detail
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void setCouponDetailIsUsed(CouponDetail detail) {

       int i= couponDetailRepository.setCouponDetailIsUsed(detail.getCouponDetailId());
       if(i==1){
           Coupon coupon =detail.getCoupon();
           couponRepository.incrementTimesUsed(coupon.getCouponId(),1);
       }else{
           throw new RuntimeException("优惠券已被使用");
       }

    }

    /**
     * 回滚优惠券
     *
     * @param detail
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void rollbackCouponDetailIsUnused(CouponDetail detail) {
        Assert.notNull(detail,"优惠券不能为空");
        Coupon coupon =detail.getCoupon();
        int i = couponDetailRepository.rollbackCouponDetailIsUnused(detail.getCouponDetailId());
        if(i!=1){
           throw new RuntimeException("回滚优惠券失败");
        }else{
            int j =couponRepository.decrementTimesUsed(coupon.getCouponId(),1);
            if(j!=1){
                throw new RuntimeException("回滚优惠券失败");
            }
        }
    }

    protected CouponDetail newCouponDetail(Coupon coupon){
        CouponDetail detail =new CouponDetail();
        BeanUtils.copyProperties(coupon,detail);
        return detail;
    }

    /**
     * 兑换优惠券
     *
     * @param customer
     * @param code
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public List<CouponResponse> redeemCoupon(Customer customer, String code) {
       List<Coupon> coupons = couponRepository.findAllByRedeemCodeAndIsEnabled(code,Boolean.TRUE);
        if (CollectionUtils.isNotEmpty(coupons)) {
            List<CouponResponse> responses =new ArrayList<>(coupons.size());
            coupons.stream().filter(coupon -> {
                return CouponReleaseMode.AUTO.equalsIgnoreCase(coupon.getReleaseMode());
            }).forEach(coupon -> {
                CouponResponse response = null;
                try {
                    response = ((CouponService) AopContext.currentProxy()).getCoupon(customer,coupon.getCouponId(), SendCouponType.BASIC);
                } catch (BonusPointsNotEnoughException e) {
                    response.setSuccess(false);
                    response.setFailureReason(e.getMessage());
                }
                responses.add(response);
            });
            return responses;
        }else{
            return null;
        }
    }

    /**
     * 根据核销码查询优惠券
     *
     * @param code
     * @return
     */
    @Override
    public CouponDetail findByConsumeCode(String code, MerchantStore store) {
        Assert.hasText(code,"消费码不能为空");
        Assert.notNull(store,"门店信息不能为空");
        return couponDetailRepository.findFirstByConsumeCodeAndReleaseBy(code,store);
    }

    /**
     * 查询用户指定的优惠券
     *
     * @param customer
     * @param id
     * @return
     */
    @Override
    public CouponDetail findCouponDetail(Customer customer, Long id) {
        return couponDetailRepository.findFirstByCustomerAndCouponDetailId(customer,id);
    }

    /**
     * 生成消费码
     *
     * @param detail
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void generateConsumeCode(CouponDetail detail) throws ConsumeCouponException {
        String flag =  detail.getCoupon().getFlag();
        if(!isActive(detail)){
           throw new ConsumeCouponException("优惠券未到使用期");
        }
        if(!isAvailable(detail)){
            throw new ConsumeCouponException("优惠券不可用");
        }
        if(!isExpiration(detail)) {
            long uid = defaultUidGenerator.getUID();
            String consumerCode = "";
            if (StringUtils.isNotBlank(flag)) {
                consumerCode = String.format("%s%d",flag,uid);
            }else{
                consumerCode = String.format("%d",uid);
            }
            Customer customer = detail.getCustomer();
            if(1 != couponDetailRepository.updateConsumeCode(detail.getCouponDetailId(),customer.getCustomerId(),consumerCode,new Date())){
                throw new ConsumeCouponException("用户优惠券不存在");
            }
            CouponHistroy histroy = new CouponHistroy(defaultUidGenerator.getUID(),
                    detail.getCouponDetailId(),
                    customer.getCustomerId(),
                    null,
                    CouponHistroy.Operation.GENERATE,
                    String.format("生成核销码：%s",detail.getConsumeCode()));
            couponHistroyRepository.save(histroy);
        }

    }
    @Override
    public boolean isAvailable(CouponDetail detail){
       return !(Boolean.TRUE.equals(detail.getIsUsed())||CouponStatus.USED.equals(detail.getCouponStatus()));
    }

    /**
     *核销码是否过期
     *  @param detail
     * @return
     */
    @Override
    public boolean isConsumeCodeExpired(CouponDetail detail){
        return (detail.getGeneratedTime().getTime()+5*60*1000)<System.currentTimeMillis();
    }

    /**
     * 优惠券是否在有效期
     * @param detail
     * @return
     */
    @Override
    public boolean isActive(CouponDetail detail){
        return DateUtils.isActive(detail.getActiveStartDate(),detail.getActiveEndDate());
    }

    /**
     * 线下核销优惠券
     * @param outTradeNo
     * @param consumeCode
     * @param totalFee
     * @throws ConsumeCouponException
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long  consumeCoupon(String outTradeNo, String consumeCode, int totalFee) throws ConsumeCouponException {
        if (StringUtils.isBlank(outTradeNo)) {
            throw new ConsumeCouponException("outTradeNo不能为空");
        }
        if (StringUtils.isBlank(consumeCode)) {
            throw new ConsumeCouponException("优惠券消费码不能为空");
        }
        if (totalFee<=0) {
            throw new ConsumeCouponException("订单金额异常："+totalFee);
        }
        CouponDetail detail = findByConsumeCode(consumeCode);
        if(detail==null){
            throw  new ConsumeCouponException("指定核销码的优惠券不存在");
        }
        if (isConsumeCodeExpired(detail)) {
            throw  new ConsumeCouponException("核销码已失效");
        }
        if(!isActive(detail)){
            if(detail.getActiveStartDate().getTime()>System.currentTimeMillis()){
                throw  new ConsumeCouponException("优惠券未到使用期");
            }else{
                throw  new ConsumeCouponException("优惠券已失效");
            }
        }
        if(!isAvailable(detail)){
            throw new ConsumeCouponException("优惠券不可用");
        }
       /* BigDecimal amount = detail.getDiscountAmount();
        BigDecimal requried = detail.getOrderMinTotal();
        int requriedFee = (requried==null?0:requried.multiply(BigDecimal.valueOf(100))).intValue();
        int amountFee = amount==null?0:(amount.multiply(BigDecimal.valueOf(100))).intValue();
        if(totalFee < requriedFee||totalFee < amountFee){
            throw  new ConsumeCouponException(String.format("订单金额不满足使用条件：最低金额%d分,面值%d分，订单金额%d分",requriedFee,amountFee,totalFee));
        }
        OrderCoupon existedOrderCoupon = orderCouponRepository.findFirstByOrderNumber(outTradeNo);
        if(existedOrderCoupon!=null){
            throw  new ConsumeCouponException("已经存在相同订单号："+outTradeNo);
        }*/
        OrderCoupon orderCoupon = new OrderCoupon();
        orderCoupon.setCouponDetail(detail);
        orderCoupon.setOrder(null);
        orderCoupon.setOrderNumber(outTradeNo);

        orderCoupon.setCouponCode(detail.getCouponCode());
        orderCoupon.setCouponPrice(detail.getDiscountAmount());
        orderCoupon.setCouponId(detail.getCoupon().getCouponId());
        orderCouponRepository.save(orderCoupon);
        setCouponDetailIsUsed(detail);
        Long transactionId = defaultUidGenerator.getUID();
        CouponHistroy histroy = new CouponHistroy(transactionId,
                detail.getCouponDetailId(),
                null,
                outTradeNo,
                CouponHistroy.Operation.CONSUME,
                String.format("核销优惠券：核销码%s,订单：%s",consumeCode,outTradeNo));
        couponHistroyRepository.save(histroy);
        return transactionId;
    }

    /**
     * 取消线下优惠券的核销（冲正）
     *
     * @param outTradeNo
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void cancleConsumeCoupon(String outTradeNo) throws CancleConsumeException {
        Assert.hasText(outTradeNo,"outTradeNo不能为空");
        List<OrderCoupon> orderCoupons =null;
        orderCoupons = orderCouponRepository.findByOrderNumber(outTradeNo);
        if(CollectionUtils.isNotEmpty(orderCoupons)){
            for (OrderCoupon orderCoupon: orderCoupons) {
                CouponDetail detail = orderCoupon.getCouponDetail();
                if(detail == null){
                    throw new CancleConsumeException("找不到相应优惠券");
                }else if(Boolean.TRUE.equals(detail.getIsUsed())&&CouponStatus.USED.equalsIgnoreCase(detail.getCouponStatus())){
                    orderCouponRepository.delete(orderCoupon);
                    rollbackCouponDetailIsUnused(detail);
                    Long transactionId = defaultUidGenerator.getUID();
                    CouponHistroy histroy = new CouponHistroy(transactionId,
                            detail.getCouponDetailId(),
                            null,
                            outTradeNo,
                            CouponHistroy.Operation.CANCEL,
                            String.format("冲销订单：%s使用的优惠券",outTradeNo));
                    couponHistroyRepository.save(histroy);
                }else{
                    logger.warn("优惠券{}在冲正时还未使用",detail.getCouponDetailId());
                }
            }
        }else{
            logger.warn("冲正订单{}时未找到相应用劵记录",outTradeNo);
        }
    }

    /**
     * 退还有优惠券
     * @param outTradeNo
     * @param transactionId
     * @throws CancleConsumeException
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Long refundCoupon(String outTradeNo, Long transactionId) throws CancleConsumeException {
        Assert.hasText(outTradeNo,"outTradeNo不能为空");
        Assert.notNull(transactionId,"系统交易号transactionId不能为空");
        CouponHistroy histroy =null;
        if(transactionId!=null){
            histroy = couponHistroyRepository.findFirstByOrderNumberAndTransactionId(outTradeNo,transactionId);
        }
        if(histroy==null){
            throw new CancleConsumeException("找不到相应记录");
        }
        else{
            if(!CouponHistroy.Operation.CONSUME.equals(histroy.getOperation())){
                throw new CancleConsumeException("优惠券状态异常:"+histroy.getOperation());
            }
            CouponHistroy returnHistroy = couponHistroyRepository.findFirstByOrderNumberAndOperation(outTradeNo, CouponHistroy.Operation.REFUND);
            if (null != returnHistroy){
                return returnHistroy.getTransactionId();
            }
            Long id = histroy.getCouponDetailId();
            CouponDetail detail = couponDetailRepository.findOne(id);
            if(detail == null){
                throw new CancleConsumeException("找不到相应优惠券");
            }else if(Boolean.TRUE.equals(detail.getIsUsed())&&CouponStatus.USED.equalsIgnoreCase(detail.getCouponStatus())){
                Long transaction= defaultUidGenerator.getUID();
                OrderCoupon orderCoupon= orderCouponRepository.findFirstByCouponDetailAndOrderNumber(detail,outTradeNo);
                if(orderCoupon==null){
                    throw new CancleConsumeException("找不到使用记录");
                }
                orderCoupon.setCanceled(true);
                rollbackCouponDetailIsUnused(detail);
                CouponHistroy newHistroy = new CouponHistroy(transaction,
                        detail.getCouponDetailId(),
                        null,
                        outTradeNo,
                        CouponHistroy.Operation.REFUND,
                        String.format("订单%s退还优惠券",outTradeNo));
                couponHistroyRepository.save(newHistroy);
                return  transaction;
            }else{
                logger.warn("相应优惠券状态异常-CouponDetailId:{}",detail.getCouponDetailId());
                throw new CancleConsumeException("相应优惠券状态异常");
            }
        }
    }
    /**
     * 退券查询
     *
     * @param outTradeNo
     * @param transactionId
     */
    @Override
    public boolean refundQuery(String outTradeNo, Long transactionId) throws CancleConsumeException {
        Assert.hasText(outTradeNo,"outTradeNo不能为空");
        Assert.notNull(transactionId,"系统交易号transactionId不能为空");
        CouponHistroy histroy =null;
        if(transactionId!=null){
            histroy = couponHistroyRepository.findFirstByOrderNumberAndTransactionId(outTradeNo,transactionId);
        }
        if(histroy==null){
            throw new CancleConsumeException("找不到相应记录");
        }else if(CouponHistroy.Operation.REFUND.equals(histroy.getOperation())){
            Long id = histroy.getCouponDetailId();
            CouponDetail detail = couponDetailRepository.findOne(id);
            if(detail == null){
                throw new CancleConsumeException("找不到相应优惠券");
            }else{
                if(Boolean.TRUE.equals(detail.getIsUsed())||CouponStatus.USED.equalsIgnoreCase(detail.getCouponStatus())){
                    return false;
                }else{
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public CouponDetail queryByConsumeCode(String code) {
        CouponDetail detail = findByConsumeCode(code);
        if(detail==null){
            return null;
        }
        CouponHistroy histroy = new CouponHistroy(defaultUidGenerator.getUID(),
                detail.getCouponDetailId(),
                null,
                null,
                CouponHistroy.Operation.QUERY,
                String.format("查询优惠券：%s（%d）",code,detail.getCouponDetailId()));
        couponHistroyRepository.save(histroy);
        return detail;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reissue(Long gameId) {

        LotteryGame lotteryGame = gameService.findById(gameId);
        //得到中奖名单  根据中奖人分组
        Map<String, List<LotteryGameWinner>> lotteryGameWinners = lotteryGame.getLotteryGameWinners().stream().collect(Collectors.groupingBy(LotteryGameWinner::getWinnerId));

        lotteryGameWinners.forEach((String opendId, List<LotteryGameWinner> winner) -> {

            LocalDateTime now = LocalDateTime.now();

            // 得到十分钟之前中奖纪录 并且是优惠券类型
            List<LotteryGameWinner> winners = winner.stream().filter(lotteryGameWinner -> {
                // 过滤出中奖时间
                LocalDateTime winDate = LocalDateUtils.dateTimeFromDate(lotteryGameWinner.getWinTime());
                return LotteryAward.AwardType.COUPON.equals(lotteryGameWinner.getLotteryAward().getAwardType())&&
                        winDate.plusMinutes(10).isBefore(now);
            }).collect(Collectors.toList());

            if(winners.isEmpty()) {
                return;
            }
            Customer customer = customerService.findByOpenId(opendId);

            //根据中奖类型分组 （不同的优惠券） 类型|中奖次数
            winners.stream().collect(Collectors.groupingBy(LotteryGameWinner::getLotteryAward)).forEach((LotteryAward lotteryAward, List<LotteryGameWinner> gameWinners) -> {
                Long awardId = lotteryAward.getId();
                // 得到理论补发次数
                int size = gameWinners.size();
                Coupon coupon = couponRepository.findOne(awardId);
                //得到实际已经补发次数
                Integer countCoupons = countCoupons(customer, coupon);
                // 进行补发
                for (int i = countCoupons; i < size ; i++) {
                    //补发优惠券
                    try {
                        getCoupon(customer,coupon.getCouponId(), SendCouponType.LOTTERY);
                    } catch (BonusPointsNotEnoughException e) {
                        logger.error(e.getErrorMsg(),e);
                    }
                }
            });
         });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAutoCouponList(String phones, String couponIds, String storeCode) {
        String[] phoneArr = phones.split(",");
        String[] couponArr = couponIds.split(",");

        for (String couponId : couponArr) {
            for (String phone : phoneArr) {
                CouponGrantAuto grantAuto = couponGrantAutoRepository.findFirstByVarious(false, phone, storeCode, Long.valueOf(couponId));
                if(grantAuto!=null){
                    logger.info("优惠期不能重复添加: {}"+grantAuto.toString());
                    continue;
                }
                CouponGrantAuto couponGrantAuto = new CouponGrantAuto();
                couponGrantAuto.setCouponId(Long.valueOf(couponId));
                couponGrantAuto.setCustomerPhone(phone);
                couponGrantAuto.setMerchantNumber(storeCode);
                couponGrantAutoRepository.save(couponGrantAuto);
            }
        }

    }

    @Override
    public Coupon findCoupon(Long couponId) {
        return couponRepository.findOne(couponId);
    }

    @Override
    public CouponDetail findByConsumeCode(String code){
        Assert.hasText(code,"消费码不能为空");
        CouponDetail detail = couponDetailRepository.findFirstByConsumeCode(code);
        return detail;
    }

    /**
     * 主动发发优惠券
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void autoGrantCoupon()  {
        List<CouponGrantAuto> couponGrantAutoList = couponGrantAutoRepository.findAllBySuccess(Boolean.FALSE,new PageRequest(0,1000));
        if (CollectionUtils.isEmpty(couponGrantAutoList)) {
            return;
        }
        for (CouponGrantAuto couponGrantAuto : couponGrantAutoList) {
               Long id = couponGrantAuto.getCouponId();
               Coupon coupon = couponRepository.findOne(id);
               if(!DateUtils.isActive(coupon.getReleaseDateStart(),coupon.getReleaseDateEnd())){
                  logger.warn("优惠券{}-{}还未到发放日期暂时不能发放",coupon.getCouponId(),coupon.getCouponName());
                   continue;
               }
                MerchantStore merchantStore = merchantStoreRepository.findFirstByExternalId(couponGrantAuto.getMerchantNumber());
                if(null != merchantStore){
                    List<Customer> customerList = customerRepository.findAllByCustomerPhoneAndMerchantStore(couponGrantAuto.getCustomerPhone(), merchantStore);
                    if(CollectionUtils.isNotEmpty(customerList) && customerList.size() == 1){
                        Customer customer = customerList.get(0);
                        CouponResponse response = null;
                        try {
                            response = getCoupon(customer, couponGrantAuto.getCouponId(), SendCouponType.BASIC);
                        }catch (BonusPointsNotEnoughException e){
                            couponGrantAuto.setFailureReason("会员积分不足");
                            logger.error(String.format("自动发券失败,用户: {%s} 积分不足:%s", couponGrantAuto.getCustomerPhone(),customer.getBonusPoints()));
                        }
                        if(null != response) {
                            if (response.isSuccess()) {
                                CouponDetail couponDetail = response.getCouponDetail();
                                couponGrantAuto.setCouponCode(couponDetail.getCouponCode());
                                couponGrantAuto.setSuccess(Boolean.TRUE);
                            }else {
                                couponGrantAuto.setFailureReason(response.getFailureReason());
                            }
                        }
                        couponGrantAutoRepository.save(couponGrantAuto);
                        }else {
                        logger.error(String.format("自动发券失败,用户: {%s}存在多个", couponGrantAuto.getCustomerPhone()));
                    }
                    }
        }
    }
}
